# Copyright 2007, Timothy Appnel, Appnel Internet
# Solutions, LLC, tim@appnel.com. All rights reserved.

package MT::Plugin::Defensio::Callbacks;
use strict;

use Net::Defensio;
use MT::Util qw(start_background_task);

# how to announce entries and pages??? how critical is this?
# what if service unresponsive? MT says ABSTAIN. USER MARKS IT JUNK??

# positive negatives right?

# later: dashboard widget, user login and trusted checking, menu item to reannounce all
# issue: mod_perl, lots of junk/no junk, entry/page import.
# trusted, logged in

# could we combine the following 2 methods? how do we know which callback has been called?

sub handle_not_junk {
    my ($cb, $app, $thing) = @_;
    my $sig = $thing->meta_column('defensio_signature') or return;
    my $blog = get_blog($thing->blog_id);
    my %params = (
                  owner-url => $blog->site_url,
                  signature => $sig,
    );
    process_task('report_false_positives', \%params);
}

sub handle_junk {
    my ($cb, $app, $thing) = @_;
    my $sig = $thing->meta_column('defensio_signature') or return;
    my $blog = get_blog($thing->blog_id);
    my %params = (
                  owner-url => $blog->site_url,
                  signature => $sig,
    );
    process_task('report_false_negatives', \%params);
}

sub announce {    # use flag that is check with score?
    my ($cb, $thing) = @_;
    if ($thing->status == 2 && ($thing->allow_comments || $thing->allow_pings))
    {
        my $a       = $thing->author;
        my $blog    = get_blog($thing->blog_id);
        my $filters = $thing->text_filters;
        push @$filters, '__default__' unless @$filters;
        my $text =
          MT->apply_text_filters($thing->text . "\n" . $thing->text_more,
                                 $filters);
        my %params = (
               owner-url => $blog->site_url,
               article_author => defined $a->nickname ? $a->nickname : $a->name,
               article_author_email => $a->email,
               article_title        => $thing->title,
               article_content      => $text,
               permalink            => $thing->permalink,
        );
        process_task('', \%params);    # announce!
    }
}

sub score {
    my $thing = shift;
    my $blog  = get_blog($thing->blog_id);
    my %params = (
                  owner-url => $blog->site_url,
                  referrer  => $ENV{HTTP_REFERER},    # mod_perl!
    );

# user-logged-in
# (optional) Whether or not the user posting the comment is logged into the blogging platform (either 'true' or 'false').
# trusted-user
# (optional) Whether or not the user is an administrator, moderator or editor of this blog; the client should pass true only if blogging platform can guarantee that the user has been authenticated and has a role of responsibility on this blog (either 'true' or 'false').
    if (ref $thing eq 'MT::Comment') {
        my $e    = $thing->entry;
        my $text =
          MT->apply_text_filters($thing->text, $blog->comment_text_filters);
        $params{user-ip}      => $thing->ip;
        $e->authored_on =~ m{(\d\d\d\d)(\d\d)(\d\d)}; # ugly
        $params{article-date} => "$1/$2/$3";
        $params{permalink}    => $e->permalink;
        $params{comment-type} => 'comment';
        $params{comment-author}       => $thing->author;
        $params{comment-author-email} => $thing->email;
        $params{comment-author-url}   => $thing->url;
        $params{comment_content}      => $text;
    } elsif (ref $thing eq 'MT::TBPing') {
        require MT::Trackback;
        my $tb = MT::Trackback->load($thing->tb_id);
        my $e  = $tb->entry;
        $params{user-ip}      => $thing->ip;
        $e->authored_on =~ m{(\d\d\d\d)(\d\d)(\d\d)}; # ugly
        $params{article-date} => "$1/$2/$3";
        $params{permalink}    => $e->permalink;
        $params{comment-type} => 'trackback';
        $params{comment-author}     => $thing->blog_name;
        $params{comment-author-url} => $thing->source_url;
        $params{comment_content} => join "\n", $thing->title, $thing->excerpt;
    } else {
        return;    # Don't know what this is. Log error?
    }
    my $defensio = get_agent();
    my $response = $defensio->audit_comment(%params);
    return ABSTAIN unless $response && $response->success;
    $thing->meta_column('defensio_signature', $response->signature);
    my $plugin    = get_plugin();
    my $config    = $plugin->get_config_hash('blog:' . $thing->blog_id);
    my $weight    = $config->{weight};
    my $spaminess = $response->spaminess;
    my $abs_score = $weight * $spaminess;
    my ($score, $grade) =
      $response->spam ? (-$abs_score, 'spam') : ($abs_score, 'ham');
    return (
            $score,
            [
             "Graded $spam with a $spaminess rating for a junk score of $score"]
    );
}

#--- utility methods

sub get_agent {
    my $r = MT->request;
    unless ($r->stash('MT::Plugin::Defensio::agent')) {
        my $plugin = get_plugin();
        my $key    = $plugin->get_config_value('api_key')
          || return;    # LOUDER???? USE CB ERROR HANDLER?
        my $v     = $plugin->version;
        my $NDV   = $Net::Defensio::VERSION;
        my $agent = LWP::UserAgent->new;
        $agent->agent("Defensio for MT $v (Net::Defensio $NDV)");
        my $cfg = MT->config;
        my $proxy = $cfg->HTTPProxy || $cfg->PingProxy;
        $agent->proxy($protcols, $proxy) if $proxy;
        my $no_proxy = $cfg->HTTPNoProxy || $cfg->PingNoProxy;
        $agent->no_proxy(split(/,\s*/, $no_proxy)) if $no_proxy;
        # my $timeout = $cfg->HTTPTimeout || $cfg->PingTimeout;
        # $agent->timeout($timeout) if $timeout;
        $agent->timeout(10); # keep it short here.
        if (my $localaddr = $cfg->HTTPInterface || $cfg->PingInterface) {
            @LWP::Protocol::http::EXTRA_SOCK_OPTS = (
                                                     LocalAddr => $localaddr,
                                                     Reuse     => 1
            );
        }
        my %params = (
                      api_key      => $key,
                      service_type => 'blog',
                      user_agent   => $agent,
        );
        my $defensio = Net::Defensio->new(%params);
        $r->stash('MT::Plugin::Defensio::agent', $defensio);
    }
    $r->stash('MT::Plugin::Defensio::agent');
}

sub process_task {
    my ($meth, $params) = @_;
    my $defensio = get_agent();
    my $task = sub {
        my $response = $defensio->$meth(%$params);
        unless ($response->is_success) {
            require MT::Plugin::Defensio::Log;
            MT::Plugin::Defensio::Log->error(
                                        "Defension $meth:" . $response->content)
              ;
        }
      }
      start_background_task($task);
}

sub get_blog {
    my $id = shift;
    return unless defined $id;
    require MT::Blog;
    return MT::Blog->load($id);
}

sub get_plugin {
    my ($plugin) = grep { $_->key eq 'MT::Plugin::Defensio' } @MT::Plugins;
    return $plugin;
}

1;
